// Based on DemoC++Plugin.cpp : Defines the entry points for the DLL application.
// also based on work done in mce_dll.
// Dave Perrow 2012
//
// Routines to handle access to the tag data store
//

#include "MP.h"


//=============================================================================================================
// Structure to hold the data from MP
//=============================================================================================================
struct MP_tags {
             char tag[MAX_TAGNAME];       // Tag name xxx or xxx.xxx
             int  tag_length;             // Length of the tag name
             int  data_type;              // type of the associated value String, 0=INT, 1=string, 2=object, 3=Date
             int  istate;                 // Associated MINT value received for this tag
             time_t tstate;               // date 
             char cstate[MAX_CSTATE+1];   // Associated string value received for this tag (or object name)
            } MP_tags [MAX_MPTAGS+1];

static int MP_tag_ptr=0;

//=============================================================================================================
// Structure to hold the translation strings to be applied to the displayed output
//=============================================================================================================
struct MP_xlate {
             char in[MAX_TAGNAME];       // string in
             char out[MAX_TAGNAME];      // string out
             int inlen,outlen;           // lengths
            } MP_xlate [MAX_MPXLATE+1];

static int MP_xlate_ptr=0;

//=============================================================================================================
// Routine to dump the MP_tags contents
//=============================================================================================================
extern void __stdcall MP_Dump()
{  int i,j;
   FILE *dp_log;

//   MP_log("MP_Dump","Start");
   if (MP_Global_Dump==1)
     { if ((dp_log=fopen("plugins\\MP_Dump.log","w"))==NULL) { printf("Failed to open dump file"); return; } 
       fprintf(dp_log," id  type     ival      len  name                                     string\n");
       fprintf(dp_log,"---- ---- ------------ ----- ---------.---------.---------.---------. ----------------------------------------------\n");

       for (i=1; i<(MP_tag_ptr+1); i++)   
         { fprintf(dp_log,"%4d  %2d %12d  %3d   %-40s %-s\n",i,MP_tags[i].data_type,MP_tags[i].istate,
                                              MP_tags[i].tag_length,MP_tags[i].tag,MP_tags[i].cstate); }
       fflush(dp_log);
       fclose(dp_log);
     }
//   MP_log("MP_Dump","End");
}


//=============================================================================================================
// Routine to clear the tag store
//=============================================================================================================
extern void __stdcall MP_Clear_tags()
{  int i,j;

  for (i=0;i<MAX_MPTAGS+1;i++)
    {  MP_tags[i].tag[0]=0;       // Tag name xxx or xxx.xxx
       MP_tags[i].tag_length=0;   // Length of the tag name
       MP_tags[i].data_type=0;    // type of the associated value String, 0=INT, 1=string, 2=object, 3=Date
       MP_tags[i].istate=0;       // Associated MINT value received for this tag
       MP_tags[i].tstate=0;       // date 
       MP_tags[i].cstate[0]=0;    // Associated string value received for this tag (or object name)
    }
}

//=============================================================================================================
// Routine to get a tag value from the store
// returns entry number (1-n) or 0
//=============================================================================================================
extern int __stdcall MP_get_tag(char *tag_name,int *data_type, int *istate, char *value)
{ int i,j;

  j=strlen(tag_name);
  *istate=0; *data_type=0; value[0]=0;  // Assume 0 returns
  
  for (i=1; i<(MP_tag_ptr+1); i++)
    { if (!strncmp(tag_name,MP_tags[i].tag,j))
        {  strcpy(value,MP_tags[i].cstate); 
           *istate=MP_tags[i].istate;
           *data_type=MP_tags[i].data_type;
           return i;
        }
    }
  return 0;
}

//=============================================================================================================
// Routine to get a tag string from the store
// returns null or tag value
//=============================================================================================================
extern int __stdcall MP_itag(char *tag_name)
{ int i,j,k; char value[MAX_STRING];
  
  k=MP_get_tag(tag_name,&i,&j,value);
  return j;
}

//=============================================================================================================
// Routine to get a tag string from the store
// returns tag string or if the tag isn't found returns string as null
//=============================================================================================================
extern char * __stdcall MP_gtag(char *tag_name)
{ int i,j;    static char value[MAX_STRING];
  
  if(MP_get_tag(tag_name,&i,&j,value)<1) value[0]=0;
  return value;
}

//=============================================================================================================
// Routine to get a tag time from the store
// returns null or tag value
//=============================================================================================================
extern time_t __stdcall MP_gttag(char *tag_name)
{ int i,j,k;   static time_t tval;  static char value[MAX_STRING];

  k=MP_get_tag(tag_name,&i,&j,value);
  if (k>0) tval=MP_tags[k].tstate; else tval=0;
  return tval;
}


//=============================================================================================================
// Routine to Update an existing tag value or create a new tag entry if it doesn't exist already
// returns entry number (1-n) or 0
//=============================================================================================================
extern int __stdcall MP_put_tag(char *tag_name, int dt, int istate, char *ivalue)
{  int i,j,type,ival, data_type;
   char str[MAX_STRING+1], str2[MAX_STRING+1], cval[MAX_STRING], value[MAX_STRING];
   time_t given;
   long long lgiven;

//   sprintf(str,"            type=%2d, ival=%6d, name=%40s, string=%s",dt,istate,tag_name,ivalue);
//   MP_logl(2,"put_tag",str);

   // Convert Dates - format supplied is in milliseconds since 1st Jan 1970 (needs a long long to store it)
   data_type=dt;
   j=strlen(ivalue); 
   if (j>MAX_CSTATE) j=MAX_CSTATE-1;
   strncpy(value,ivalue,j); value[j]=0;
   if (j>7)
     { if (!strncmp(value,"\\/Date(",7))
         { // Convert the date
           if (isdigit(value[7]))
             { sscanf(&value[7],"%lld",&lgiven);
               lgiven=lgiven/1000;
               given=lgiven;
             } 
           else given=0;  
           time_fmt(value,given);
           data_type=3;
         }
     }

  // Check if the tag already exists
   i=MP_get_tag(tag_name, &type, &ival, cval);
   if (i==0)   // Not found so create a new entry
     { i=MP_tag_ptr+1; 
       j=strlen(tag_name);
       strncpy(MP_tags[i].tag,tag_name,j);
       MP_tags[i].tag[j]=0;                        // Fill in the tag name
       MP_tags[i].tag_length=j;                    // its length
       MP_tags[i].data_type=data_type;             // data type
       MP_tags[i].istate=istate;
       j=strlen(value);                            // limit the length of the string value
       if (j>MAX_CSTATE) j=MAX_CSTATE-1;
       strncpy(MP_tags[i].cstate,value,j);         // fill it in
       MP_tags[i].cstate[j]=0;                     // null terminated
       if (data_type==3) MP_tags[i].tstate=given;  // copy the time if it's a type 3
       MP_tag_ptr=MP_tag_ptr+1;                    // Use the next entry
       if (i>MAX_MPTAGS) MP_tag_ptr=MP_tag_ptr-1;  // If gone past the end ignore it
     }  
   else                                                // Update the existing tag values
     { j=strlen(value);                                // limit the length of the string value
       if (j>MAX_CSTATE) j=MAX_CSTATE-1;
       if (j>0) 
         { strncpy(MP_tags[i].cstate,value,j);         // fill it in
           MP_tags[i].cstate[j]=0;                     // null terminated
           MP_tags[i].data_type=data_type;             // data type
           MP_tags[i].istate=istate;
           if (data_type==3) MP_tags[i].tstate=given;  // copy the time if it's a type 3
         }
     }
   sprintf(str,"entry=%4d, type=%2d, ival=%6d, name=%40s, string=%s",i+1,dt,istate,tag_name,ivalue);
   MP_logl(2,"put_tag",str);

   return i;
}
           
//=============================================================================================================
// Main routine to parse the incoming string into tags and values
//=============================================================================================================
extern void __stdcall MP_Parse(char *mod, char *line)
{  int lptr,i,j,k, nested;
   char tagname[MAX_TAGNAME];

   lptr=0; nested=0;
   for (i=0; i<MAX_TAGNAME+1;i++) tagname[i]=0;
   MP_Parse_it(mod, tagname, line, &lptr, &nested);
}

//=============================================================================================================
// Routine to parse the incoming string into tags and values
// runs in a loop till we get to the end of the input message
//=============================================================================================================   
extern void __stdcall MP_Parse_it(char *mod,char *tagname, char *line, int *lptr, int *nested)
{  int i,j,k;
   char cstate[MAX_CSTATE+1];

   while(*lptr<strlen(line))
    { i=strfind(line,'{',*lptr);  // is there more?
      if (i>0) { *lptr=i-1; MP_lock(); MP_Parse_item(mod,tagname,line,lptr,nested); MP_unlock(); }
      else break;
    }
}   

//=============================================================================================================
// Routine to parse a tags section 
// e.g. [{"Tag":"#TV.View.channel","Value":"ITV2"},{"Tag":"#TV.View.start","Value":"13:30"}]
// {"Type":"propertychanged","Tag":"#TV.View.title","Value":"Wolf"}
// lptr points to { on entry
//=============================================================================================================   
extern void __stdcall MP_Parse_tags(char *mod,char *tagname,char *line, int *lptr)
{ int i,tstart,tend,tlen,taglen; 
  int vstart,vend,vlen;
  char tname[MAX_STRING], vval[MAX_STRING];
  char str[MAX_STRING];
 
  while ((line[*lptr]=='{') || (line[*lptr]==','))
    { tstart=*lptr+9; tend=strfind(line,'"',tstart)-2; tlen=tend-tstart+1;
      vstart=tend+12; vend=strfind(line,'"',vstart)-2; vlen=vend-vstart+1;
      if (vlen>0)
        { strcpy(tname,tagname); taglen=strlen(tagname);
          strncpy(&tname[taglen],&line[tstart],tlen); tname[taglen+tlen]=0; // Remove the #
          strncpy(vval,&line[vstart],vlen); vval[vlen]=0;
          i=MP_put_tag(tname,1,0,vval);           // store this
        }
      *lptr=vend+4;                               // point to next { or past eol
      if((*lptr>strlen(line)) || (line[*lptr]==0)) return;
    }
  if (line[*lptr]!=0) *lptr=*lptr+1;                     // ignore ]
}    

//=============================================================================================================
// Routine to parse the incoming string into tags and values
// recursive Passed string should start with { or [
// Example: {"Type":"status","IsPlaying":false,"IsPaused":false,"IsPlayerOnTop":false,"Title":"",
//                            "CurrentModule":"Basic home","SelectedItem":"TV Series","WindowId":35}
//=============================================================================================================
extern void __stdcall MP_Parse_item(char *mod,char *tagname, char *line, int *lptr, int *nested)
{  int i,j,k;
   int tstart,tend,tlen;
   int valstart,valend,vallen;
   char cstate[MAX_CSTATE+1];
   char itagname[MAX_TAGNAME],jtagname[MAX_TAGNAME];
   int ival;
   char str[MAX_STRING];

   if ((line[*lptr]!='{') && (line[*lptr]!='[') && (line[*lptr]!=',')){ *lptr=*lptr+1; return; }
   *lptr=*lptr+1;
   if ((line[*lptr]=='}') || (line[*lptr]==']') || (line[*lptr]==',') || (line[*lptr]=='\r') || 
       (line[*lptr]=='\n') || (line[*lptr]==0)   || *lptr>strlen(line))
         { *lptr=*lptr+1; return; }  // exit if empty string



//   MP_log("Parse item"," called");
   while(*lptr<strlen(line))
    {
//       sprintf(str,"name=%s, lptr=%d, nested=%d, line=%s",tagname,*lptr,*nested,&line[*lptr]);
//       MP_log("Parse Item: ",str);
       if ( (line[*lptr]==',') || (line[*lptr]=='{') ){ *lptr=*lptr+1; }
       
//       if (line[*lptr]==',') { *lptr=*lptr+1; break; }
       if ((line[*lptr]=='}') || (line[*lptr]==']')|| (line[*lptr]=='\r')||
           (line[*lptr]=='\n')|| (line[*lptr]==0)  || *lptr>strlen(line))
         { *lptr=*lptr+1; break; }  // exit if empty string
       if (line[*lptr]=='{') break;
       if (line[*lptr]!='\"') { sprintf(str,"Illegal character code %d found at %d on line:",line[*lptr],*lptr);
                                i=strlen(str); if((strlen(line)+i)>MAX_STRING-1) i=strlen(line)-i-1;
                                strncat(str,line,i); str[i]=0;
                                MP_errlog("Parse_item:",str); *lptr=*lptr+1; break; }

       tstart=*lptr+1;                // pointer to start of tag name (after quote)
       tend=strfind(line,'"',tstart);  // find the end of the tag name
       if (tend<0) { sprintf(str,"Cant find terminating quote for tagname at %d",*lptr);
                     MP_errlog("Parse_item:",str); *lptr=*lptr+1; break; ; }
       tend=tend-2; tlen=tend-tstart+1;           

       // tag name now in line[tptr] to line[k-1]
       if (*nested==0) { strncpy(itagname,&line[tstart],tlen); itagname[tlen]=0; }
           else { strncpy(jtagname,&line[tstart],tlen); jtagname[tlen]=0; 
                  strcpy(itagname,tagname); strcat(itagname,"."); strcat(itagname,jtagname); }
//       MP_log("Parse_item: tag name:",itagname);

       // Point the lptr to the quote after the : character
       *lptr=tend+3;

       // itagname is now the tag name    
       // now get the tag value this can be a quoted string, a list (starting with [), a number or true,false,null
       if (line[*lptr]=='[')                  // array
         { // Tags list
           // Example: {"Type":"properties","Tags":[{"Tag":"#currentplaytime","Value":"52:24"},
           //          {"Tag":"#currentremaining","Value":"1:38:59"}]}
           if (!strncmp(itagname,"Tags",4)) { *lptr=*lptr+1; MP_Parse_tags(mod,"Properties.",line,lptr); }
           else if (line[*lptr+1]==']') { *lptr=*lptr+2; break; }
           else { valstart=*lptr; valend=strfind(line,']',valstart)-1; vallen=valend-valstart+1;  // find the closing ]
                  strncpy(str,&line[valstart],vallen); str[vallen]=0;
                  MP_put_tag(itagname,0,0,str);                      // Store the whole list as a single entry     
                  *lptr=valend+1;                                    // point past ]         
                }
         }  
       else if (!strncmp(itagname,"Tag",3)) { *lptr=*lptr-7; MP_Parse_tags(mod,"Properties.",line,lptr); }
       else if ((isdigit(line[*lptr])) || (line[*lptr]=='-'))               // integer value
         { i=*lptr;
           sscanf(&line[i],"%d",&ival); 
           while ((isdigit(line[i])) || (line[i]=='-')) i++;
           sprintf(str,"%d",ival);
           MP_put_tag(itagname,0,ival,str);
           *lptr=i;
         }
       else if (!strncmp(&line[*lptr],"false",5)){ MP_put_tag(itagname,1,0,"false"); *lptr=*lptr+5; }
       else if (!strncmp(&line[*lptr],"true",4)) { MP_put_tag(itagname,1,1,"true");  *lptr=*lptr+4; }
       else if (!strncmp(&line[*lptr],"null",4)) { MP_put_tag(itagname,1,0,"null");  *lptr=*lptr+4; }
       else if (line[*lptr]=='{')                  // object
         { *nested=strlen(itagname); 
           strncpy(tagname,itagname,*nested);
           MP_Parse_item(mod,tagname,line,lptr,nested);
           *nested=0; tagname[0]=0;
         }
       else if (line[*lptr]=='"') 
         { valstart=*lptr+1; valend=strfind(line,'"',valstart)-2; vallen=valend-valstart+1;
           if (vallen>0)                            // string value from i+1 to j-2
                 { i=vallen;
                   if (i>MAX_CSTATE) i=MAX_CSTATE;
                   strncpy(cstate,&line[valstart],i); cstate[i]=0;
                   MP_put_tag(itagname,1,0,cstate);
                 }
            *lptr=valend+3;                        // poit past closing quote
         }
       else { MP_errlog("Parse item: Unknown character-",&line[*lptr]); *lptr=strlen(line)+1; } // dunno what this is          
    }
//  MP_log("Parse item ","complete");
}


//=============================================================================================================
// Routine to find a translation string
//=============================================================================================================
extern void __stdcall MP_xlate_find(char *in, char *out)
{ int i,j;
  char str[MAX_STRING];

  strcpy(out,in);    
  for (i=0;i<MP_xlate_ptr;i++)
    { if(!strncmp(in,MP_xlate[i].in,MP_xlate[i].inlen))
        { strncpy(out,MP_xlate[i].out,MP_xlate[i].outlen); out[MP_xlate[i].outlen]=0; break; }
    }
}

//=============================================================================================================
// Routine to store a translation string
//=============================================================================================================
extern void __stdcall MP_xlate_store(char *in, char *out, int inlen, int outlen)
{ char str[MAX_STRING];

  strcpy(MP_xlate[MP_xlate_ptr].in,in);
  strcpy(MP_xlate[MP_xlate_ptr].out,out);
  MP_xlate[MP_xlate_ptr].inlen=inlen;
  MP_xlate[MP_xlate_ptr].outlen=outlen;
  MP_xlate_ptr=MP_xlate_ptr+1;
}

